--local cjson = require("cjson.safe")

local inspect = require("inspect")
local ffi = require("ffi")
local bit = require "bit"
local sub = string.sub
local strbyte = string.byte
local strsub = string.sub
local strchar = string.char
local format = string.format
local tab_insert = table.insert
local tab_remove = table.remove
local ipairs = ipairs
local type = type
local pairs = pairs
local unpack = unpack
local setmetatable = setmetatable
local tonumber = tonumber
local tostring = tostring
local rawget = rawget
local select = select
local band = bit.band
local bxor = bit.bxor
local bor = bit.bor
local lshift = bit.lshift
local rshift = bit.rshift
local concat = table.concat
local game = ""

local int64 = ffi.typeof("int64_t")
local uint64 = ffi.typeof("uint64_t")

-- require("libtolua")

local ok, new_tab = pcall(require, "table.new")
if not ok or type(new_tab) ~= "function" then
    new_tab = function(narr, nrec) return {} end
end

local _M = new_tab(0, 100)
_M._VERSION = '0.0.1'


local mt = { __index = _M }

local function _dump(data)
    local len = #data
    local bytes = new_tab(len, 0)
    for i = 1, len do
        bytes[i] = format("%x,", strbyte(data, i))
    end
    return concat(bytes, " ")
end

local function _get_byte4(data, i)
    local d, c, b, a = strbyte(data, i, i + 3)
    return bor(a, lshift(b, 8), lshift(c, 16), lshift(d, 24)), i + 4
end

local function _get_byte2(data, i)
    local b, a = strbyte(data, i, i + 1)
    return bor(a, lshift(b, 8)), i + 2
end

local function _get_byte(data, i)
    return strbyte(data, i), i + 1
end

local function shl64(n, bits)
  local hi = n.hi * 2^bits + bit.rshift(n.lo, 32-bits)
  local lo = bit.lshift(n.lo, bits)
  return uint64(lo, hi)
end

local function tint64(v2, v1)
    return int64(v2) + int64(v1) + int64(4294967296)
end



--local function tint64(v2,v1)
--    if v2 < 0 then return tostring(int64.new(v2 + 4294967296,v1))
--    else return tostring(int64.new(v2,v1))
--    end
--end

local function _get_byte8(data, i)
    local l, i = _get_byte4(data, i)
    local r, i = _get_byte4(data, i)
    return tint64(r, l), i, r
end

local function find_i64(hkey, rid, ridLow)
    local f = hkey[ridLow]
    if f then
        for _k, v in ipairs(f) do
            if v[1] == rid then
                return v[2]
            end
        end
        local newp = hkey.sp + 1
        hkey.sp = newp
        local newt = { rid, newp }
        tab_insert(f, newt)
        return newp
    end
    local newp = hkey.sp + 1
    hkey.sp = newp
    hkey[ridLow] = { { rid, newp } }
    return newp
end

local function camp_pos(camp, pos)
    if camp == 1 then
        return pos
    end
    if camp == 2 then
        return 9 - pos
    end
end

--<<?VSNID:8,FightID:32,GSFID:32,Seed:32,Role1:64,Role2:64>>
local function decode_header(data, pos)
    local vsn, pos = _get_byte(data, pos)
    local fightID, pos = _get_byte4(data, pos)
    local gsfightID, pos = _get_byte4(data, pos)
    local seed, pos = _get_byte4(data, pos)
    local rl1, pos = _get_byte8(data, pos)
    local rl2, pos = _get_byte8(data, pos)
    local challengeType, pos = _get_byte2(data, pos)
    if vsn == 1 then
        return { vsn, fightID, seed, rl1, rl2, challengeType }, pos
    elseif vsn == 2 then
        local battleTs, pos = _get_byte4(data, pos)
        return { vsn, fightID, seed, rl1, rl2, challengeType, battleTs, gsfightID}, pos
    elseif vsn == 3 then
        local battleTs, pos = _get_byte4(data, pos)
        local dungeonID, pos = _get_byte4(data, pos)
        return { vsn, fightID, seed, rl1, rl2, challengeType, battleTs, gsfightID, dungeonID}, pos
    end
end

-- attr k v
local function decode_buff(data, pos)
    local buf = {}
    local rt = 0
    local rt, pos1 = _get_byte2(data, pos)
    if rt ~= 21001 then
        return {}, pos
    end
    local cnt
    cnt, pos = _get_byte2(data, pos1)
    local t = 0
    local k = 0
    local v1 = 0
    local v2 = 0
    while cnt > 0 do
        --t,pos = _get_byte(data,pos)
        k, pos = _get_byte2(data, pos)
        v1, pos = _get_byte4(data, pos)
        --if t == 0 then
        if k > 32767 then
            tab_insert(buf, { band(k, 0x7fff), v1 * 0.0001 })
        else
            tab_insert(buf, { k, v1 })
        end
        --elseif t == 1 then
        --    v2,pos = _get_byte4(data,pos)
        --    tab_insert(buf,{k,tint64(v2,v1)})
        --end
        cnt = cnt - 1
    end
    return buf, pos
end

-- true buff
local function decode_buffid(data, pos)
    local list = {}
    local cnt = 0
    pos = pos + 2
    cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local id = 0
        id, pos = _get_byte2(data, pos)
        tab_insert(list, { id })
        cnt = cnt - 1
    end
    return list, pos
end

local function decode_mon_role(data, pos)
    local _, pos = _get_byte4(data, pos)
    return 0, pos
end

local function decode_mon_arg(data, pos)
    local typ = 0
    local args = {}
    local cnt = 2
    while cnt > 0 do
        typ, pos = _get_byte2(data, pos)
        if typ == 11031 then
            local fp = 0
            fp, pos = _get_byte8(data, pos)
            args.fightPower = fp
        elseif typ == 11032 then
            local rage = 0
            local buff = 0
            rage, pos = _get_byte2(data, pos)
            args.rage = rage
            buff, pos = decode_buffid(data, pos)
            args.rageBuff = buff
        else
            pos = pos - 2
            break
        end
        cnt = cnt - 1
    end
    return args, pos
end

local function c_ger(uid, rl_i64, tid, lv, breakUp, x, y, quality, skills, modInfos, factor, scale, grade, star, fightPower, exclusive_rank, skinID, dragonSpirit, isBoss, talent, awake)
    local t = {
        uid = uid,
        rl_i64 = rl_i64,
        typeID = tid,
        level = lv,
        grade = grade,
        breakUpTime = breakUp,
        row = x,
        col = y,
        rank = star,
        quality = quality,
        attr = { modInfos = modInfos, modFactor = factor * 0.01 },
        skills = skills,
        equips = {},
        weapons = {},
        roleWeapons = {},
        fightPower = fightPower,
        exclusive_rank = exclusive_rank,
        dragonSpirit = dragonSpirit,
        skinID = skinID,
        isBoss = isBoss,
        talent = talent,
        awake = awake

    }
    if scale then
        t.scale = { x = scale * 0.0001, y = scale * 0.0001, z = scale * 0.0001 }
    end
    return t
end

local function decode_mon_ger(data, pos, basefactor)
    local cnt, pos = _get_byte2(data, pos)
    local gers = {}
    while cnt > 0 do
        local tid = 0
        local lv = 0
        local id = 0
        local x = 0
        local y = 0
        local factor = 0
        local scale = 0
        local quality = 0
        local isBoss = 0
        local skill = {}
        local buff = {}
        tid, pos = _get_byte4(data, pos)
        lv, pos = _get_byte2(data, pos)
        id, pos = _get_byte2(data, pos)
        x, pos = _get_byte(data, pos)
        y, pos = _get_byte(data, pos)
        factor, pos = _get_byte4(data, pos)
        scale, pos = _get_byte2(data, pos)
        quality, pos = _get_byte2(data, pos)
        isBoss, pos = _get_byte(data, pos)
        buff, pos = decode_buff(data, pos)
        tab_insert(gers, c_ger(tint64(id, 0), id, tid, lv, 0, x, y, quality, skill, buff, basefactor or factor, scale, 0, 0, tint64(0, 0), 0, 0, {}, isBoss, {}, 0))
        cnt = cnt - 1
    end
    return gers, pos
end

local function decode_buildings_ger(data, pos)
    local cnt, pos = _get_byte2(data, pos)
    local gers = {}
    while cnt > 0 do
        local tid = 0
        local lv = 0
        local id = 0
        local x = 0
        local y = 0
        local factor = 0
        local scale = 0
        local skill = {}
        local buff = {}
        tid, pos = _get_byte4(data, pos)
        lv, pos = _get_byte2(data, pos)
        id, pos = _get_byte2(data, pos)
        x, pos = _get_byte(data, pos)
        y, pos = _get_byte(data, pos)
        factor, pos = _get_byte4(data, pos)
        scale, pos = _get_byte2(data, pos)
        buff, pos = decode_buff(data, pos)
        tab_insert(gers, c_ger(tint64(id, 0), id, tid, lv, 0, x, y, 0, skill, buff, factor, scale, 0, 0, tint64(0, 0), 0, 0, {}, 0, {},0))
        cnt = cnt - 1
    end
    return gers, pos
end

local function decode_multi_mons(data, pos)
    local cnt
    local monType
    local duration
    monType, pos = _get_byte4(data, pos)
    duration, pos = _get_byte2(data, pos)
    cnt, pos = _get_byte2(data, pos)
    local monTab = {}
    local total = cnt
    while cnt > 0 do
        local mons = {}
        local typ
        typ, pos = _get_byte2(data, pos)
        if typ == 11012 then
            local factor
            factor, pos = _get_byte4(data, pos)
            pos = pos + 6
            mons, pos = decode_mon_ger(data, pos, factor)
        elseif typ == 11011 then
            pos = pos + 6
            mons, pos = decode_mon_ger(data, pos)
        end
        cnt = cnt - 1
        local wave = { dungeonID = total - cnt, isBoss = 0, heroes = mons }
        tab_insert(monTab, wave)
    end
    return monTab, pos, monType, duration
end

local function decode_role(data, pos, hkey, ver)
    local rid, pos, roleIDLow = _get_byte8(data, pos)
    local level, pos = _get_byte2(data, pos)
    local head, pos = _get_byte4(data, pos)
    local headframe, pos = _get_byte4(data, pos)
    local birth, pos = _get_byte4(data, pos)
    local kingomID, pos = _get_byte4(data, pos)
    local serverID, pos = _get_byte4(data, pos)
    local nameLen, pos = _get_byte2(data, pos)
    local name = strsub(data, pos, pos + nameLen - 1)
    pos = pos + nameLen
    local familyName = ""
    if ver >= 3 then
        local familyNameLen = 0
        familyNameLen, pos = _get_byte2(data, pos)
        familyName = strsub(data, pos, pos + familyNameLen - 1)
        pos = pos + familyNameLen
    end
    local designation = 0
    if ver >= 4 then
        designation, pos = _get_byte4(data, pos)
    end
    local vipLevel = 0
    if  ver>=8 then
        vipLevel,pos = _get_byte2(data,pos)
    end
    local info = {
        uid = rid,
        level = level,
        name = name,
        head = head,
        headframe = headframe,
        kingomID = kingomID,
        serverID = serverID,
        birth = birth,
        familyName = familyName,
        designation = designation,
        vipLevel = vipLevel,
        offlineTs = 0
    }
    return info, pos
end

local function read_ger_skills(data, pos)
    local skills = {}
    pos = pos + 2
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local skillID = 0
        local lv = 0
        local rk = 0
        skillID, pos = _get_byte4(data, pos)
        lv, pos = _get_byte2(data, pos)
        rk, pos = _get_byte(data, pos)
        tab_insert(skills, { skillID = skillID, level = lv, rank = rk })
        cnt = cnt - 1
    end
    return skills, pos
end

local function read_ger_skins(data, pos)
    pos = pos + 2
    local cnt, pos = _get_byte2(data, pos)
    local tid = 0
    tid, pos = _get_byte4(data, pos)
    return tid, pos
end

local function read_ger_dragonSpirit(data, pos)
    pos = pos + 2
    local cnt = 0
    cnt, pos = _get_byte2(data, pos)
    local spirit = {}
    while cnt > 0 do
        local id = 0
        local state = 0
        local level = 0
        local rank = 0
        id, pos = _get_byte(data, pos)
        state, pos = _get_byte(data, pos)
        level, pos = _get_byte2(data, pos)
        rank, pos = _get_byte(data, pos)
        tab_insert(spirit, { id = id, state = state, level = level, breakUpTime = rank })
        cnt = cnt - 1
    end
    return spirit, pos
end

local function read_ger_talent(data, pos)
    pos = pos + 2
    local cnt = 0
    cnt, pos = _get_byte2(data, pos)
    local talents = {}
    while cnt > 0 do
        local id = 0
        local level = 0
        id, pos = _get_byte4(data, pos)
        level, pos = _get_byte4(data, pos)
        tab_insert(talents, { id = id, level = level})
        cnt = cnt - 1
    end
    return talents, pos
end

local function decode_trainer(data, pos)
    local moduleID = 0
    local modInfos, skills = {}
    moduleID, pos = _get_byte4(data, pos)
    modInfos, pos = decode_buff(data, pos)
    skills, pos = read_ger_skills(data, pos)
    local t = {
        roleTypeID = moduleID,
        attr = { modInfos = modInfos },
        skills = skills
    }
    return t, pos
end

local function decode_mounts_skills(data, pos)
    local list = {}
    local cnt, rt = 0, 0
    rt, pos = _get_byte2(data, pos)
    cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local id = 0
        id, pos = _get_byte4(data, pos)
        tab_insert(list, { id })
        cnt = cnt - 1
    end
    return list, pos
end

local function decode_mounts(data, pos)
    local mountID, isActivePattern, skills = 0, 0, {}
    mountID, pos = _get_byte4(data, pos)
    isActivePattern, pos = _get_byte(data, pos)
    skills, pos = decode_mounts_skills(data, pos)
    local t = {
        mountID = mountID,
        skills = skills,
        isActivePattern = isActivePattern == 1
    }
    return {t}, pos
end

local function decode_ger(data, pos, hkey, ver)
    local cnt, pos = _get_byte2(data, pos)
    local gers = {}
    local tmp = {}
    while cnt > 0 do
        local tid = 0
        local typ = 0
        local lv = 0
        local rank = 0
        local star = 0
        local quality = 0
        local breakUp = 0
        local skills = tmp
        local skinID = 0
        local modInfos = tmp
        local rl_i64 = 0
        local tidr = 0
        local fightPower = 0
        local exclusive_rank = 0
        local scale = 0
        local grade = 0
        local dragonSpirit = tmp
        local talent = {}
        local awake = 0
        tid, pos, tidr = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        lv, pos = _get_byte2(data, pos)
        quality, pos = _get_byte2(data, pos)
        breakUp, pos = _get_byte2(data, pos)
        star, pos = _get_byte2(data, pos)
        fightPower, pos = _get_byte8(data, pos)
        exclusive_rank, pos = _get_byte2(data, pos)
        scale, pos = _get_byte2(data, pos)
        grade, pos = _get_byte4(data, pos)
        if ver >= 7 then
            awake, pos = _get_byte2(data, pos)
        end
        modInfos, pos = decode_buff(data, pos)
        skills, pos = read_ger_skills(data, pos)
        skinID, pos = read_ger_skins(data, pos)
        dragonSpirit, pos = read_ger_dragonSpirit(data, pos)
        if ver >= 5 then
            talent, pos = read_ger_talent(data, pos)
        end

        rl_i64 = find_i64(hkey, tid, tidr)
        tab_insert(gers, c_ger(tid, rl_i64, typ, lv, breakUp, 0, 0, quality, skills, modInfos, 100, scale, grade, star, fightPower, exclusive_rank, skinID, dragonSpirit, 0, talent, awake))
        cnt = cnt - 1
    end
    return gers, pos
end

local function decode_player_skill(data, pos)
    pos = pos + 2
    local cnt, pos = _get_byte2(data, pos)
    local skills = {}
    while cnt > 0 do
        local skillID = 0
        local lv = 0
        skillID, pos = _get_byte4(data, pos)
        lv, pos = _get_byte2(data, pos)
        tab_insert(skills, { skillID = skillID, level = lv })
        cnt = cnt - 1
    end
    return skills, pos
end

local function decode_roleweapon_tips(data, pos)
    local cnt, pos = _get_byte2(data, pos)
    local tips = {}
    local tipID = 0
    local attrID = 0
    local val = 0
    while cnt > 0 do
        tipID, pos = _get_byte2(data, pos)
        attrID, pos = _get_byte2(data, pos)
        val, pos = _get_byte2(data, pos)
        tab_insert(tips, { tipID = tipID, attrID = attrID, val = val })
        cnt = cnt - 1
    end
    return tips, pos
end

local function decode_roleweapon(data, pos)
    local roleweapons = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local wid = 0
        local typ = 0
        local rnk = 0
        local qua = 0
        wid, pos = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        rnk, pos = _get_byte2(data, pos)
        qua, pos = _get_byte2(data, pos)
        tab_insert(roleweapons, { weaponUID = wid, weaponTypeID = typ, rank = rnk, quality = qua })
        cnt = cnt - 1
    end
    return roleweapons, pos
end

local function decode_ger_roleweapon(data, pos, hkey)
    local gerRoleWeaponTab = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local gidr
        local rl_i64
        local gerRW
        local gerUID
        gerUID, pos, gidr = _get_byte8(data, pos)
        gerRW, pos = decode_roleweapon(data, pos)
        rl_i64 = find_i64(hkey, gerUID, gidr)
        gerRoleWeaponTab[rl_i64] = { gerUID = gerUID, rl_i64 = rl_i64, weapons = gerRW }
        cnt = cnt - 1
    end
    return gerRoleWeaponTab, pos
end

local function decode_weapon(data, pos, ver)
    local weapons = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local wid = 0
        local typ = 0
        local rnk = 0
        local level = 1
        local refine = 1
        local enchant = 1
        wid, pos = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        rnk, pos = _get_byte2(data, pos)
        if ver >= 2 then
            level, pos = _get_byte2(data, pos)
            refine, pos = _get_byte2(data, pos)
            enchant, pos = _get_byte2(data, pos)
        end
        tab_insert(weapons, { weaponUID = wid, weaponTypeID = typ, weaponRank = rnk, weaponLevel = level, weaponRefine = refine, weaponEnchant = enchant})
        cnt = cnt - 1
    end
    return weapons, pos
end

local function decode_ger_weapon(data, pos, hkey, ver)
    local gerWeaponTab = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local gidr
        local rl_i64
        local gerRW
        local gerUID
        gerUID, pos, gidr = _get_byte8(data, pos)
        gerRW, pos = decode_weapon(data, pos, ver)
        rl_i64 = find_i64(hkey, gerUID, gidr)
        gerWeaponTab[rl_i64] = { gerUID = gerUID, rl_i64 = rl_i64, weapons = gerRW }
        cnt = cnt - 1
    end
    return gerWeaponTab, pos
end

local function decode_rune(data, pos)
    local runes = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local wid = 0
        local typ = 0
        local exp = 0
        local star = 0
        wid, pos = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        exp, pos = _get_byte4(data, pos)
        star, pos = _get_byte2(data, pos)
        tab_insert(runes, { runeUID = wid, runeTypeID = typ, runeExp = exp, runeStar=star })
        cnt = cnt - 1
    end
    return runes, pos
end

local function decode_ger_rune(data, pos, hkey)
    local gerRuneTab = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local gidr
        local rl_i64
        local gerRune
        local gerUID
        gerUID, pos, gidr = _get_byte8(data, pos)
        gerRune, pos = decode_rune(data, pos)
        rl_i64 = find_i64(hkey, gerUID, gidr)
        gerRuneTab[rl_i64] = { gerUID = gerUID, rl_i64 = rl_i64, weapons = gerRune }
        cnt = cnt - 1
    end
    -- print("rune cnt = ", cnt, inspect(gerRuneTab))
    return gerRuneTab, pos
end

local function decode_stigmata_entry(data, pos)
    local entryID = 0
    local lock = 0
    local attr = {}
    local slot = 0
    entryID, pos = _get_byte8(data, pos)
    lock, pos = _get_byte(data, pos)
    slot, pos = _get_byte2(data, pos)
    attr, pos = decode_buff(data, pos)
    return {entryUID=entryID, lock=lock, slot=slot, buff=attr}, pos
end

local function decode_stigmata(data, pos)
    local stigmatas = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local wid = 0
        local typ = 0
        local lv = 0
        local baseEntrys = {}
        local entrys = {}
        local baseEntryLen = 0
        local baseEntry = {}
        local entryLen = 0
        local entry = {}
        wid, pos = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        lv, pos = _get_byte4(data, pos)
        baseEntryLen, pos = _get_byte2(data, pos)
        while baseEntryLen > 0 do
            baseEntry, pos = decode_stigmata_entry(data, pos)
            tab_insert(baseEntrys, baseEntry)
            baseEntryLen = baseEntryLen - 1
        end
        entryLen, pos = _get_byte2(data, pos)
        while entryLen > 0 do
            entry, pos = decode_stigmata_entry(data, pos)
            tab_insert(entrys, entry)
            entryLen = entryLen - 1
        end
        tab_insert(stigmatas, { equipUID = wid, equipTypeID = typ, level = lv, baseEntrys = baseEntrys, entrys = entrys })
        cnt = cnt - 1
    end
    return stigmatas, pos
end

local function decode_ger_stigmata(data, pos, hkey)
    local gerStigmataTab = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local gidr
        local rl_i64
        local gerStigmata
        local gerUID
        gerUID, pos, gidr = _get_byte8(data, pos)
        gerStigmata, pos = decode_stigmata(data, pos)
        rl_i64 = find_i64(hkey, gerUID, gidr)
        gerStigmataTab[rl_i64] = { gerUID = gerUID, rl_i64 = rl_i64, weapons = gerStigmata }
        cnt = cnt - 1
    end
    -- print("stigmata cnt = ", cnt, inspect(gerStigmataTab))
    return gerStigmataTab, pos
end

local function decode_gem(data, pos)
    local gems = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local uid = 0
        local typ = 0
        local weapon_uid = 0
        uid, pos = _get_byte8(data, pos)
        typ, pos = _get_byte4(data, pos)
        weapon_uid , pos = _get_byte8(data, pos)
        tab_insert(gems, { gemUID = uid, gemTypeID = typ, weaponUID=weapon_uid})
        cnt = cnt - 1
    end
    return gems, pos
end


local function decode_ger_gem(data, pos, hkey)
    local gerGemTab = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local gidr
        local rl_i64
        local gerGem
        local gerUID
        gerUID, pos, gidr = _get_byte8(data, pos)
        gerGem, pos = decode_gem(data, pos)
        rl_i64 = find_i64(hkey, gerUID, gidr)
        gerGemTab[rl_i64] = { gerUID = gerUID, rl_i64 = rl_i64, weapons = gerGem}
        cnt = cnt - 1
    end
    return gerGemTab, pos
end

local function decode_outbuff(data, pos, args)
    local rage = 0
    local buff = 0
    pos = pos + 2
    rage, pos = _get_byte2(data, pos)
    args.rage = rage
    buff, pos = decode_buffid(data, pos)
    args.rageBuff = buff
    args.otherBuff, pos = decode_buff(data, pos)
    return args, pos
end

local function decode_dynamic_buff(data, pos, args, battleTs)
    local dyns = {}
    local skills = {}
    local cnt, pos = _get_byte2(data, pos)
    local skillcnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local id = 0
        local ts = 0
        local buff = {}
        local pcnt = 0
        id, pos = _get_byte2(data, pos)
        ts, pos = _get_byte4(data, pos)
        pcnt, pos = _get_byte2(data, pos)
        while pcnt > 0 do
            _, pos = _get_byte2(data, pos)
            pcnt = pcnt - 1
        end
        buff, pos = decode_buff(data, pos)
        if ts == 0 or ts >= battleTs then
            dyns[id] = buff
        end
        cnt = cnt - 1
    end
    while skillcnt > 0 do
        local id = 0
        local skill = {}
        id, pos = _get_byte2(data, pos)
        skill, pos = read_ger_skills(data, pos)
        skills[id] = skill
        skillcnt = skillcnt - 1
    end
    args.dynamic = { attrs = dyns, skills = skills }
    return args, pos
end

local function decode_stand_info(data, pos, hkey)
    local stands = {}
    local cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        local spos = 0
        local guid = 0
        local guidr = 0
        local rl_i64 = 0
        guid, pos, guidr = _get_byte8(data, pos)
        rl_i64 = find_i64(hkey, guid, guidr)
        spos, pos = _get_byte2(data, pos)
        rawset(stands, rl_i64, { rshift(spos, 4), band(spos, 15), guid, guidr })
        cnt = cnt - 1
    end
    return stands, pos
end

local function decode_role_arg(data, pos, args)
    local typ = 0
    local cnt = 0
    cnt, pos = _get_byte2(data, pos)
    while cnt > 0 do
        typ, pos = _get_byte2(data, pos)
        if typ == 30001 then
            local fp = 0
            fp, pos = _get_byte8(data, pos)
            args.fightPower = fp
        end
        cnt = cnt - 1
    end
    return args, pos
end

--[[

local function bprint(data,pos)
    local i = _get_byte(data,pos)
    return i
end
]] --

local function get_header_role(header, camp)
    if camp == 1 then return header[4]
    else return header[5]
    end
end

local function decode_player_info(roleID, camp, data, pos, total, header)
    local typ = 0
    local cmdtyp = 0
    local n = 0
    local tmp = {}
    local hkey = { sp = 0 }
    local player = {
        role = { roleID = roleID },
        ger = {},
        buildings = {},
        roleWeapons = {},
        fightType = 0,
        waves = {},
        args = {},
        weapons = {},
        runes = {},
        trainer = {},
        stigmata = {},
        gems = {},
        mounts = {}
    }
    local mons = {}
    local duration = 0
    local typ, pos = _get_byte2(data, pos)
    local ver, pos1 = _get_byte2(data, pos)
    if ver < 10000 then
        pos = pos1
    else
        -- 老数据没有ver字段，默认为1
        ver = 1
    end
    print("ver = ", ver)
    while pos < total do
        typ, pos = _get_byte2(data, pos)
        print("type = ", typ)
        if typ == 23001 then
            local d = 0
            d, pos = decode_role(data, pos, hkey, ver)
            player.role = d
        elseif typ == 10201 then
            local ts = 0
            ts, pos = _get_byte4(data, pos)
            player.role.offlineTs = ts
        elseif typ == 17001 then
            local d = 0
            d, pos = decode_ger(data, pos, hkey, ver)
            player.ger = d
        elseif typ == 20001 then
            local d = 0
            d, pos = decode_ger_roleweapon(data, pos, hkey)
            player.roleWeapons = d
        elseif typ == 19001 then
            local d = 0
            d, pos = decode_ger_weapon(data, pos, hkey, ver)
            player.weapons = d
        elseif typ == 32001 then
            local d = 0
            d, pos = decode_ger_rune(data, pos, hkey)
            player.runes = d
        elseif typ == 32002 then
            if ver >= 5 then
                local d = {}
                d, pos = decode_ger_stigmata(data, pos, hkey)
                player.stigmata = d
            end
        elseif typ == 32003 then
            if ver >= 6 then
                local d = {}
                d, pos = decode_ger_gem(data, pos, hkey)
                player.gems = d
            end
        elseif typ == 11001 then
            local d = 0
            n, pos = decode_mon_role(data, pos)
            --player.role=d
        elseif typ == 11003 then
            local d = 0
            n, pos = decode_mon_role(data, pos)
            d, pos = decode_mon_arg(data, pos)
            player.args = d
        elseif typ == 11010 then
            local d = 0
            local fightType = 0
            mons, pos, fightType, duration = decode_multi_mons(data, pos)
            player.fightType = fightType
            player.waves = { mons, duration }
            player.ger = mons[1].heroes
        elseif typ == 11020 then
            local monType = 0
            monType, pos = _get_byte4(data, pos)
            local d = 0
            mons, pos = decode_mon_role(data, pos)
            player.ger = mons
            player.fightType = monType
        elseif typ == 11002 then
            local d = 0
            d, pos = decode_mon_ger(data, pos)
            player.ger = d
        elseif typ == 11004 then
            local d = 0
            d, pos = decode_buildings_ger(data, pos)
            player.buildings = d
        elseif typ == 22001 then
            local d = 0
            d, pos = decode_stand_info(data, pos, hkey)
            player.stand = d
        elseif typ == 24001 then
            player.args, pos = decode_role_arg(data, pos, player.args)
        elseif typ == 23002 then
            player.trainer, pos = decode_trainer(data, pos)
        elseif typ == 32004 then
            player.mounts, pos = decode_mounts(data, pos)
        elseif typ == 31001 then
            player.args, pos = decode_outbuff(data, pos, player.args)
        elseif typ == 31002 then
            local battleTs = 0
            if header ~= 0 then
                battleTs = header[7]
            end
            player.args, pos = decode_dynamic_buff(data, pos, player.args, battleTs)
        else
            pos = pos - 2
            break
        end
    end
    return player, pos
end


local function get_ger_stand(uid, stand)
    return stand[uid]
end

local function remove_ger_stand(uid, stand)
    stand[uid] = nil
end

local function rebuild_stand(player, camp)
    local tmp = {}
    if player.stand then
        for k, v in pairs(player.stand) do
            --local col = camp_pos(camp,v[2])
            --local row = camp_pos(camp,v[1])
            --tab_insert(tmp,{row,col,v[3],v[4]})
            tab_insert(tmp, { camp_pos(camp, v[1]), camp_pos(camp, v[2]), v[3], v[4] })
        end
    end
    return tmp
end

local function merge_addList(modInfos, addList)
    if not addList then
        return attrs
    end
    local v0 = 0
    for k, v in pairs(addList) do
        v0 = modInfos[k] or 0
        modInfos[k] = v0 + v
    end
    return modInfos
end

local function merge_meds_buf(attrs, idstr, meds)
    if not meds then
        return attrs
    end
    local modInfos = merge_addList(attrs.modInfos, meds[idstr])
    attrs.modInfos = modInfos
    return attrs
end

local function merge_buff(attrs, addList)
    if not addList then
        return attrs
    end
    local modInfos = attrs.modInfos
    for _, v in ipairs(addList) do
        tab_insert(modInfos, table.clone(v))
    end
    attrs.modInfos = modInfos
    return attrs
end

local function get_kval(data,type)
    if not data then
        return {}
    end
    return data.weapons
end

local function build_hero(player)

    local p = {}
    local id = 0
    local t = 0
    local stand = player.stand
    local meds = player.meds
    local weaponsTab = player.weapons
    local otherBuff = player.args.otherBuff
    local roleWeaponTab = player.roleWeapons
    local runesTab = player.runes
    local stigmataTab = player.stigmata
    local gemsTab = player.gems
    local isMons = false
    if not stand or not next(stand) then
        isMons = true
    end
    for _, g in ipairs(player.ger) do
        local idstr = g.rl_i64
        local isStand = false
        --g.attr = merge_meds_buf(g.attr,idstr,meds)
        g.weapons = get_kval(weaponsTab[idstr])
        g.roleWeapons = get_kval(roleWeaponTab[idstr])
        g.runes = get_kval(runesTab[idstr])
        g.stigmata = get_kval(stigmataTab[idstr])
        g.gems = get_kval(gemsTab[idstr])
        if stand then
            local pos = get_ger_stand(idstr, stand)
            if pos then
                g.attr = merge_buff(g.attr, otherBuff)
                g.col = pos[2]
                g.row = pos[1]
                isStand = true
                remove_ger_stand(idstr, stand)
            end
        end
        if isMons or isStand then
            tab_insert(p, g)
        end
    end
    return p
end

local function build_buildings(player)
    local p = {}
    for _, g in ipairs(player.buildings) do
        g.weapons = {}
        g.roleWeapons = {}
        tab_insert(p, g)
    end
    return p
end



local function build_hero_with_camp(player, camp)
    local p = {}
    local id = 0
    local t = 0
    local stand = player.stand
    --local meds = player.meds
    local weapons = player.weapons
    local roleWeapon = player.roleWeapons
    local runesTab = player.runes
    local stigmataTab = player.stigmata
    local gemsTab = player.gems
    local otherBuff = player.args.otherBuff
    for _, g in ipairs(player.ger) do
        local idstr = g.rl_i64
        --  g.attr = merge_meds_buf(g.attr,idstr,meds)
        g.weapons = get_kval(weapons[idstr])
        g.roleWeapons = get_kval(roleWeapon[idstr])
        g.runes = get_kval(runesTab[idstr])
        g.gems = get_kval(gemsTab[idstr])
        g.stigmata = get_kval(stigmataTab[idstr])
        if stand then
            local pos = get_ger_stand(idstr, stand)
            if pos then
                g.attr = merge_buff(g.attr, otherBuff)
                g.col = camp_pos(camp, pos[2])
                g.row = camp_pos(camp, pos[1])
                remove_ger_stand(idstr, stand)
            end
        end
        tab_insert(p, g)
    end
    return p
end

local function build_reinforce(player)
    if player.fightType == 0 then
        return {}
    end
    local all = player.waves[1]
    tab_remove(all, 1)
    return { waveInfos = all, rageWaitDuration = player.waves[2] }
end

local function decode_fighter(header, p, data, camp, pos, total)
    local roleID = get_header_role(header, p)
    local player_i, pos = decode_player_info(roleID, camp, data, pos, total, header)
    local heroes = build_hero_with_camp(player_i, camp)
    local buildings = build_buildings(player_i)
    --local reinforce = build_reinforce(player_i)
    local stand = rebuild_stand(player_i, camp)
    return { camp = camp, heroes = heroes, buildings = buildings,
        args = player_i.args, stand = stand, role = player_i.role,
        trainer = player_i.trainer, mounts = player_i.mounts }, pos
end

local function get_forceTimeout(challenge_type,dungeonID)
    if challenge_type == 117 then
        local config = game.configMgr:loadResourceChapterDungeonTable()
        return config[dungeonID].combat_duration
    end
    return 0
end

--{vsn,fightID,seed,rl1,rl2}
function _M.decode_msg(outterGame, battleType, playerType, enemyType, data)
    game = outterGame
    local pos = 1
    local header, pos = decode_header(data, pos)
    local total = #data
    local team1, pos = decode_fighter(header, 1, data, playerType, pos, total)
    local team2, pos = decode_fighter(header, 2, data, enemyType, pos, total)
    if total + 1 ~= pos then
        return nil, nil, string.format("decode not finish,%d,%d", total, pos)
    end
    return header, {
        type = battleType,
        mode_type = battleType,
        challenge_type = header[6],
        random = { seed = header[3] },
        players = { team1, team2 },
        view = { enabled = false },
        ui = { enabled = false },
        forceTimeout = get_forceTimeout(header[6],header[9]),
    }
end

function _M.decode_player_role(data, battleTs)
    local battleTs = battleTs or 0
    local header = {[7]=battleTs}
    local pos = 1
    local camp = 1
    local total = #data
    local player_i, pos = decode_player_info(0, 0, data, pos, total, header)
    local heroes = build_hero(player_i)
    local stand = rebuild_stand(player_i, camp)
    return { heroes = heroes, role = player_i.role, args = player_i.args,
        trainer = player_i.trainer, stand = stand, mounts = player_i.mounts }, pos
end

local function _set_byte_3(a, b, c)
    return strchar(band(a, 0xff), band(b, 0xff), band(c, 0xff))
end

local function _set_byte2(n)
    return strchar(band(rshift(n, 8), 0xff), band(n, 0xff))
end

local function _set_byte(n)
    return strchar(band(n, 0xff))
end

local function _set_byte4(n)
    return strchar(band(rshift(n, 24), 0xff),
        band(rshift(n, 16), 0xff),
        band(rshift(n, 8), 0xff),
        band(n, 0xff))
end

function _M.encode_result(header, win, retInfo, ver)
    local ret = ""
    local camp = 0
    for _, d in ipairs(retInfo.players) do
        camp = d.camp
        ret = ret .. _set_byte(camp) .. _set_byte(#d.units)
        for i, ger in ipairs(d.units) do
            local l, h = int64.tonum2(ger.uid)
            ret = ret .. _set_byte4(h) .. _set_byte4(l) .. _set_byte4(ger.damage) .. _set_byte4(ger.hp)
        end
    end

    if retInfo.result == win then
        return _set_byte(1) .. _set_byte4(ver) .. _set_byte4(header[2]) .. _set_byte4(header[8]) .. _set_byte4(retInfo.runTime) .. ret
    end
    return _set_byte(2) .. _set_byte4(ver) .. _set_byte4(header[2]) .. _set_byte4(header[8]) .. _set_byte4(retInfo.runTime) .. ret
end

local function decode_fightpower(data, pos)
    local fp = 0
    fp, pos = _get_byte8(data, pos)
    return fp, pos
end

local function decode_update_roleID(data, pos)
    local roleID = 0
    local serverID = 0
    roleID, pos = _get_byte8(data, pos)
    serverID, pos = _get_byte2(data, pos)
    return { roleID, serverID }, pos
end


function _M.decode_player_change(data)
    local pos = 1
    local typ = 0
    typ, pos = _get_byte2(data, pos)
    if typ ~= 10102 then
        return "bad packet"
    end
    local total = #data
    local roleInfo = 0
    roleInfo, pos = decode_update_roleID(data, pos)
    local tmp = {}
    local hkey = { sp = 0 }
    local player = { role = roleInfo, ger = tmp, roleWeapons = tmp, players = tmp }
    while pos < total do
        typ, pos = _get_byte2(data, pos)
        if typ == 17001 then
            local d = 0
            d, pos = decode_ger(data, pos, hkey, 100)
            player.ger = d
        elseif typ == 20001 then
            local d = 0
            d, pos = decode_ger_roleweapon(data, pos, hkey)
            player.roleWeapons = d
        elseif typ == 22001 then
            local d = 0
            d, pos = decode_stand_info(data, pos, hkey)
            player.stand = d
        else
            pos = pos - 2
            break
        end
    end
    return player, hkey, pos
end

function _M.decode_dynamic_info(data, ts)
    local id = _get_byte2(data, 1)
    if id ~= 31002 then
        return nil, "bad dynamic data"
    end

    local ret = decode_dynamic_buff(data, 3, {}, ts)
    return ret.dynamic
end

return _M

